package com.example.vpnservicedemo;

import android.content.Intent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;


import com.example.vpnservicedemo.receive.Pipe;
import com.example.vpnservicedemo.receive.TcpPipe;
import com.example.vpnservicedemo.receive.UdpPipe;
import com.example.vpnservicedemo.utils.PushPc;

import org.pcap4j.packet.IpPacket;
import org.pcap4j.packet.IpSelector;
import org.pcap4j.packet.Packet;

import org.pcap4j.packet.TransportPacket;

import org.pcap4j.packet.namednumber.IpNumber;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;

public class ReadVpn {
    private ParcelFileDescriptor descriptor;
    private VpnService vpnService;

    private Thread readDataThread;

    private PushPc pushPc;

    private WriteVpn writeVpn;

    private Integer receiveLen = 0;

    public ReadVpn(VpnService vpnService, ParcelFileDescriptor descriptor) {
        this.vpnService = vpnService;
        this.descriptor = descriptor;

        pushPc = new PushPc(vpnService);
        writeVpn = new WriteVpn(descriptor);

    }

    public void start() {

        //创建一个线程读取channel数据
        readDataThread = new Thread(() -> {

            FileChannel readChannel = null;
            ExecutorService threadPool = null;
            try {


                // 获取一个channel，零拷贝读取数据
                readChannel = new FileInputStream(descriptor.getFileDescriptor()).getChannel();


                ByteBuffer buffer = ByteBuffer.allocate(1024 * 20);


                while (!Thread.interrupted()) {

                    int len = readChannel.read(buffer);

                    if (len == -1) {
                        break;
                    }

                    if (len > 0) {
                        buffer.flip();
                        byte[] bt = new byte[len];
                        buffer.get(bt);

                        //计算接收数量
                        receiveLen += len;
                        //发送到ui
                        pushUi();

                        //推送PC
                        pushPc.pushData(bt);

                        parseData(bt);

                        buffer.clear();

                    }

                }

            } catch (Exception e) {

                System.out.println("ReadData start 异常: " + e.getMessage());

            } finally {

                if (readChannel != null) {
                    try {
                        readChannel.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }

                if (threadPool != null) {
                    threadPool.shutdown();
                }
            }
        });

        readDataThread.setName("readDataThread");
        readDataThread.start();
    }

    private Map<String, Pipe> pipeMap = new ConcurrentHashMap<>();

    private void parseData(byte[] array) {
        try {
            Packet packet = IpSelector.newPacket(array, 0, array.length);
            if (packet instanceof IpPacket) {

                IpPacket ipPacket = (IpPacket) packet;
                //通过源ip和源port判断唯一SendData
                String key = getKey(ipPacket);

                if (key != null) {

                    if (pipeMap.get(key) == null) {
                        Pipe pipe = null;
                        if (ipPacket.getHeader().getProtocol() == IpNumber.TCP) {
                            pipe = new TcpPipe(vpnService, this);
                        } else if (ipPacket.getHeader().getProtocol() == IpNumber.UDP) {
                            pipe = new UdpPipe(vpnService, this);
                        }
                        if (pipe != null) {
                            //防止多线程问题
                            pipeMap.putIfAbsent(key, pipe);
                        }
                    }

                    Pipe pipe = pipeMap.get(key);

                    int code = pipe.receive(ipPacket);

                    if (code == -1) {
                        //说明关闭了
                        pipeMap.remove(key);
                    }

                }

            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("ReadData parseData 解析包异常:" + e.getMessage());
        }

    }

    private void pushUi() {
        Intent broadcastIntent = new Intent("com.example.UPDATE_TEXT");
        broadcastIntent.putExtra("read", this.receiveLen.toString());
        this.vpnService.sendBroadcast(broadcastIntent);

    }

    public void pushVpn(byte[] bytes) {

        Intent broadcastIntent = new Intent("com.example.UPDATE_TEXT");
        broadcastIntent.putExtra("write", this.receiveLen.toString());
        this.vpnService.sendBroadcast(broadcastIntent);

        pushPc.pushData(bytes);
        writeVpn.push(bytes);
    }


    private String getKey(IpPacket ipPacket) {
        String key = null;

        Packet payload = ipPacket.getPayload();
        if (payload instanceof TransportPacket) {
            InetAddress srcAddr = ipPacket.getHeader().getSrcAddr();
            TransportPacket transportPacket = (TransportPacket) payload;
            int srcPort = transportPacket.getHeader().getSrcPort().valueAsInt();

            key = srcAddr.getHostAddress() + srcPort;

        }
        return key;
    }


    public void stop() {
        if (readDataThread != null) {
            readDataThread.interrupt();
        }
    }
}
